package ms.unicorn.dsl.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Platform;
import org.osgi.framework.Bundle;

public class ReflectionUtil {
	
	public static List<Bundle> bundles = new ArrayList<Bundle>();
	public static Map<String, Class> classes = new HashMap<String, Class>();
	public static Map<Class, Object> objects = new HashMap<Class, Object>();
	
	public static <T extends Object> T getSingletonObject(String className) {
		Class clazz = getClass(className);
		if (clazz != null) {
			Object o = objects.get(clazz);
			if (o == null) {
				try {
					o = clazz.getConstructor().newInstance();
					objects.put(clazz, o);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			return (T)o;
		}
		return null;
	}
	
	public static void registerBundle(String pluginID) {
		Bundle bundle = Platform.getBundle(pluginID);
		if (bundle != null && bundles.indexOf(bundle) < 0)
			bundles.add(bundle);
			
	}
	
	public static Class<?> getClass(String className) {
		Class<?> clazz = null;
		try {
			clazz = classes.get(className);
			if (clazz == null)
				clazz = Class.forName(className);
		} catch (Exception e) {
			if (className != null && className.length() > 0)
				for(Bundle bundle: bundles)
					try {
						clazz = bundle.loadClass(className);
						classes.put(className, clazz);
						return clazz;
					}
					catch(Exception e1) {}
		}
		return clazz;
	}
	
	public static Class<?> getClass(String className, ClassLoader loader) {
		Class<?> clazz = null;
		try {
			clazz = Class.forName(className, true, loader);
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return clazz;
	}
	
	public static Class<?> getClass(String className, Class<?> defaultClass) {
		Class<?> clazz = getClass(className);
		return clazz != null ? clazz : defaultClass;
	}
	
	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... params) {
		Method med;
		try {
			med = clazz.getMethod(methodName, params);
		} catch (Exception e) {
			med = null;
		}
		return med;
	}
	
	public static Object invoke(Method method, Object target, Object...params) {
		try {
			return method.invoke(target, params);
		}
		catch (InvocationTargetException e) {
			if (e.getTargetException() instanceof RuntimeException)
				throw (RuntimeException)e.getTargetException();
			else throw new RuntimeException(e);
		}
		catch(SphynxRuntimeException e) {
			throw e;
		}
		catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	private static Class<?> resolveClass(Object target) {
		if (target instanceof Class<?>)
			return (Class<?>) target;
		if (target != null)
			return target.getClass();
		return null;
	}
	
	static final String[] getPrefixes = new String[] {"get", "is", ""}; 
	
	@SuppressWarnings("unchecked")
	public static <T> T getPropertyValue(Object target, String propertyName, T defaultValue) {
		Class<?> clazz = resolveClass(target);
		if (clazz != null) {
			for (int i = 0; i < getPrefixes.length; i++)
				try {
					Method m = clazz.getMethod(getPrefixes[i] + propertyName);
					return (T)m.invoke(target);
				}
				catch (Exception e) {}
			try {
				Field field = clazz.getField(propertyName);
				return (T) field.get(target);
			}
			catch (Exception e) {
				return defaultValue;
			}
		}
		return defaultValue;
	}
	
	public static void setPropertyValue(Object target, String propertyName, Object value) {
		int idx = propertyName.indexOf('.');
		if (idx > 0) {
			Object newTarget = getPropertyValue(target, propertyName.substring(0, idx), null);
			String newPropertyName = propertyName.substring(idx + 1);
			setPropertyValue(newTarget, newPropertyName, value);
		}
		else  {
			Class<?> clazz = resolveClass(target);
			try {
				Field field = clazz.getField(propertyName);
				field.set(target, value);	
			}
			catch (Exception e) {
				try {
					String medName = "set" + propertyName;
					for(Method m: clazz.getMethods())
						if (m.getName().equalsIgnoreCase(medName)) {
							m.invoke(target, value);
							return;
						}
				} catch (Exception e2) {}
			}
		}
	}

	public static Object getField(Object owner, String featureName, Object defaultValue) {
		// TODO Auto-generated method stub
		return null;
	}

	public static boolean isIntegerNumber(Object value) {
		return value instanceof Integer || value instanceof Byte || value instanceof Short;
	}
	
}
